local skynet = require "skynet"
local filename = "slotmgr.lua"
local filelog = require "filelog"
local queue = require "skynet.queue"
local base = require "base"
local redisdao = require "redisdao"
local tabletool = require "tabletool"
local json = require "cjson"
json.encode_sparse_array(true,1,1)
local sg = require "sg"

local SlotTool = require "slottool"
require "sgenum"

------------
--枚举类

--系统输钱上限基值,默认10万
local SystemLoseLimit_Base = 1000000

--系统输钱上限控制系数
local SystemLoseLimit_Factor = 0.08

--开奖类型
local LotteryType = {
    LOTTERY_TYPE_SYSTEM_WIN = 1,   --系统必赢
    LOTTERY_TYPE_SYSTEM_LOSE = 2,  --系统必输
    LOTTERY_TYPE_NORMAL = 3,       --正常开奖
}

--图标类型
local Picture_type = {
    PICTURE_TYPE_NORMAL = 1,    --普通图标
}

--图标subid（不计类型）
local Picture_subid = {
    101,    --闲家1赢
    102,    --闲家2赢
    103,    --闲家3赢
    104,    --庄家通杀
}

--图标id
local Picture_id = {
    1101,    --闲家1赢
    1102,    --闲家2赢
    1103,    --闲家3赢
    1104,    --庄家通杀
}

--特殊奖励定义
local SpecialPrize = {
    SPECIAL_PRIZE_SANPAI = 0,   --散牌
    SPECIAL_PRIZE_BADIAN = 1,   --八点
    SPECIAL_PRIZE_JIUDIAN = 2,  --九点
    SPECIAL_PRIZE_SANGONG = 3,  --三公
    SPECIAL_PRIZE_SANTIAO = 4,  --三条
    SPECIAL_PRIZE_BAOSAN = 5,   --爆三
}

--额外奖励类型列表（所有奖励）
--sgenum.lua中有对应定义
local Extra_prize_type = {

}

----------------------
--数据表，可以改变

--倍率列表
--key为：图标id = 图标类型 + 图标subid
--[1101] = 0 表示：红的赔率为2倍
--
local Odds_list = {
    [1101] = {  --闲1赢
        [SpecialPrize.SPECIAL_PRIZE_SANPAI] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BADIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_JIUDIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANGONG] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANTIAO] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BAOSAN] = 2,
    },     
    [1102] = {  --闲2赢
        [SpecialPrize.SPECIAL_PRIZE_SANPAI] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BADIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_JIUDIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANGONG] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANTIAO] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BAOSAN] = 2,
    },    
    [1103] = {  --闲3赢
        [SpecialPrize.SPECIAL_PRIZE_SANPAI] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BADIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_JIUDIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANGONG] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANTIAO] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BAOSAN] = 2,
    },    
    [1104] = {  --庄通杀
        [SpecialPrize.SPECIAL_PRIZE_SANPAI] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BADIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_JIUDIAN] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANGONG] = 2,
        [SpecialPrize.SPECIAL_PRIZE_SANTIAO] = 2,
        [SpecialPrize.SPECIAL_PRIZE_BAOSAN] = 2,
    }   
}

--图标权重列表
--共8种开奖：
-- {闲1赢}，{闲2赢}，{闲3赢}，{闲1赢，闲2赢}，{闲1赢，闲3赢}，
-- {闲2赢，闲3赢}，{闲1赢，闲2赢，闲3赢}，{庄通杀}
local Weight_list = {
    [2100] = 400,    --闲1赢
    [2010] = 400,    --闲2赢
    [2001] = 400,    --闲3赢
    [2110] = 200,    --闲1,2赢
    [2101] = 200,    --闲1,3赢
    [2011] = 200,    --闲2,3赢
    [2111] = 100,    --闲1,2,3赢
    [2000] = 50,     --庄通杀
}

--权值项对应倍率列表
local Witem_odds_list = {
    [2100] = {1101},            --闲1赢
    [2010] = {1102},            --闲2赢
    [2001] = {1103},            --闲3赢
    [2110] = {1101,1102},       --闲1,2赢
    [2101] = {1101,1103},       --闲1,3赢
    [2011] = {1102,1103},       --闲2,3赢
    [2111] = {1101,1102,1103},  --闲1,2,3赢
    [2000] = {1104},            --庄通杀
}

--系统库存概率表
--S = 库存值
local S_list = {
    [1] = { -- S < -500万
        nohit = 1.00,   --必不中概率
        normal = 0.00,  --正常开奖概率
        hit = 0.00,     --必中概率
    },
    [2] = { -- -500万<=S<-100万
        nohit = 0.80,   --必不中概率
        normal = 0.20,  --正常开奖概率
        hit = 0.00,     --必中概率
    },
    [3] = { -- -100万<=S<-20万
        nohit = 0.60,   --必不中概率
        normal = 0.40,  --正常开奖概率
        hit = 0.00,     --必中概率
    },
    [4] = { -- -20万<=S<-10万
        nohit = 0.30,   --必不中概率
        normal = 0.70,  --正常开奖概率
        hit = 0.00,     --必中概率
    },
    [5] = { -- -10万<=S<200万
        nohit = 0.00,   --必不中概率
        normal = 1.00,  --正常开奖概率
        hit = 0.00,     --必中概率
    },
    [6] = { -- 200万<=S<1000万
        nohit = 0.00,   --必不中概率
        normal = 0.80,  --正常开奖概率
        hit = 0.20,     --必中概率
    },
    [7] = { -- 1000万<=S<5000万
        nohit = 0.00,   --必不中概率
        normal = 0.60,  --正常开奖概率
        hit = 0.40,     --必中概率
    },
    [8] = { -- 5000万<=S<1亿
        nohit = 0.00,   --必不中概率
        normal = 0.50,  --正常开奖概率
        hit = 0.50,     --必中概率
    },
    [9] = { -- S>=1亿
        nohit = 0.00,   --必不中概率
        normal = 0.40,  --正常开奖概率
        hit = 0.60,     --必中概率
    },
}

--特殊奖励权值表
local SPrize_weight_list = {
    
}

--开奖权值表
local Jackpot_weight_list = {
    [0] = {
        [1] = 100,  --中奖励1的权重
        [2] = 0,    --中奖励2的权重
        [3] = 0,    --中奖励3的权重
        [4] = 0,    --中奖励4的权重
        [5] = 0,    --中奖励5的权重
        [6] = 0,    --中奖励6的权重
    },
    [1] = {
        [1] = 80,   --中奖励1的权重
        [2] = 20,   --中奖励2的权重
        [3] = 0,    --中奖励3的权重
        [4] = 0,    --中奖励4的权重
        [5] = 0,    --中奖励5的权重
        [6] = 0,    --中奖励6的权重
    },
    [2] = {
        [1] = 60,   --中奖励1的权重
        [2] = 30,   --中奖励2的权重
        [3] = 10,   --中奖励3的权重
        [4] = 0,    --中奖励4的权重
        [5] = 0,    --中奖励5的权重
        [6] = 0,    --中奖励6的权重
    },
    [3] = {
        [1] = 50,   --中奖励1的权重
        [2] = 30,   --中奖励2的权重
        [3] = 10,   --中奖励3的权重
        [4] = 10,   --中奖励4的权重
        [5] = 0,    --中奖励5的权重
        [6] = 0,    --中奖励6的权重
    },
    [4] = {
        [1] = 40,   --中奖励1的权重
        [2] = 30,   --中奖励2的权重
        [3] = 20,   --中奖励3的权重
        [4] = 10,   --中奖励4的权重
        [5] = 0,    --中奖励5的权重
        [6] = 0,    --中奖励6的权重
    },
    [5] = {
        [1] = 30,   --中奖励1的权重
        [2] = 30,   --中奖励2的权重
        [3] = 20,   --中奖励3的权重
        [4] = 10,   --中奖励4的权重
        [5] = 10,   --中奖励5的权重
        [6] = 0,    --中奖励6的权重
    },
    [6] = {
        [1] = 10,   --中奖励1的权重
        [2] = 20,   --中奖励2的权重
        [3] = 20,   --中奖励3的权重
        [4] = 20,   --中奖励4的权重
        [5] = 20,   --中奖励5的权重
        [6] = 10    --中奖励6的权重
    },
}

--奖池奖励列表
local Jackpot_award_list = {
    [1] = 1,    --获取奖池1%
    [2] = 5,    --获取奖池5%
    [3] = 8,    --获取奖池8%
    [4] = 10,   --获取奖池10%
    [5] = 15,   --获取奖池15%
    [6] = 20,   --获取奖池20%
}


--------------------------
--辅助表

--图标概率表，由权重表生成
local Prob_list = nil

--图标总权值,，由权重表生成
local Total_weight = nil

--预存牌型表,每种5种
local Profile_Cards_List = {
    [2100] = {      --闲1赢
        [1] = {{3,5},{2,7},{1,4},{6,8} },   --8 9 5 4
        [2] = {{2,4},{1,8},{3,2},{10,3} },  --6 9 5 3
        [3] = {{1,6},{10,8},{7,5},{4,2} },  --7 8 2 6
        [4] = {{1,8},{10,9},{3,5},{2,4} },  --9 9 8 6
        [5] = {{2,5},{1,7},{10,6},{9,4} },  --7 8 6 3
    },            
    [2010] = {      --闲2赢
        [1] = {{8,7},{10,4},{3,6},{5,9} },  --5 4 9 4
        [2] = {{2,5},{6,7},{10,8},{3,9} },  --7 3 8 2
        [3] = {{9,7},{3,8},{4,5},{1,2} },   --6 1 9 3
        [4] = {{6,1},{1,4},{5,3},{9,2} },   --7 5 8 1
        [5] = {{3,2},{4,7},{4,3},{10,1} },  --5 1 7 1
    },            
    [2001] = {      --闲3赢
        [1] = {{1,5},{2,10},{3,2},{6,3} },  --6 2 5 9
        [2] = {{9,5},{1,2},{6,8},{4,3} },   --4 3 4 7
        [3] = {{2,4},{10,3},{1,10},{7,2} }, --6 3 1 9
        [4] = {{2,5},{3,9},{8,7},{4,5} },   --7 2 5 9
        [5] = {{2,6},{10,7},{3,5},{1,8} },  --8 7 8 9
    },            
    [2110] = {      --闲1,2赢
        [1] = {{3,5},{1,8},{2,6},{4,7} },   --8 9 8 1
        [2] = {{4,2},{8,1},{3,4},{7,6} },   --6 9 7 3
        [3] = {{6,1},{8,10},{7,2},{3,4} },  --7 8 9 7
        [4] = {{3,2},{5,4},{8,7},{10,1} },  --5 9 5 1
        [5] = {{3,4},{1,7},{5,2},{6,9} },   --7 8 7 5
    },       
    [2101] = {      --闲1,3赢
        [1] = {{10,6},{2,7},{9,7},{3,5} },  --6 9 6 8
        [2] = {{3,1},{8,7},{2,9},{10,7} },  --4 5 1 7
        [3] = {{2,5},{9,8},{5,10},{1,7} },  --7 7 5 8
        [4] = {{2,3},{3,6},{1,10},{5,4} },  --5 9 1 9
        [5] = {{1,5},{8,9},{7,6},{2,7} },   --6 7 3 9
    },       
    [2011] = {      --闲2,3赢
        [1] = {{10,2},{3,9},{4,5},{1,6} },  --2 2 9 7
        [2] = {{4,1},{7,5},{8,7},{6,2} },   --5 2 5 8
        [3] = {{4,2},{10,5},{1,8},{9,7} },  --6 5 9 6
        [4] = {{9,6},{2,1},{10,7},{4,5} },  --5 3 7 9
        [5] = {{10,3},{7,4},{2,5},{8,1} },  --3 1 7 9
    },       
    [2111] = {      --闲1,2,3赢
        [1] = {{9,5},{3,4},{7,8},{6,2} },   --4 7 5 8
        [2] = {{4,10},{3,5},{8,7},{10,9} }, --4 8 5 9
        [3] = {{7,6},{4,3},{10,6},{2,5} },  --3 7 6 7
        [4] = {{2,3},{10,6},{5,1},{9,8} },  --5 6 6 7
        [5] = {{1,2},{2,5},{6,3},{8,10} },  --3 7 9 8
    },  
    [2000] = {      --庄通杀
        [1] = {{9,10},{4,5},{3,2},{1,7} },  --9 9 5 8
        [2] = {{8,9},{2,4},{1,10},{7,5} },  --7 6 1 2
        [3] = {{4,5},{1,6},{7,8},{3,2} },   --9 7 5 5
        [4] = {{1,7},{10,5},{7,6},{2,9} },  --8 5 3 1
        [5] = {{6,2},{3,4},{5,9},{10,1} },  --8 7 4 1
    },            
}

-------------------------------
-------------------------------
---工具类


--更新图标概率表
function SlotTool.update_picture_prob_list()
    local prob_list = {}

    local total_prob = 0
    local total_weight = SlotTool.calculate_total_weight(Weight_list)
    for id,w in pairs(Weight_list) do
        prob_list[id] = w / total_weight
        total_prob = total_prob + prob_list[id]
    end
        
    --总概率应为1
    if math.floor(total_prob * 100) ~= 100 then
        filelog.sys_error("total probability not equal to 1!",total_prob)
    end

    return prob_list,total_weight
end

--
--获取开奖类型
--返回 LotteryType类型
--
function SlotTool.get_lottery_type(gamename)
    --获取库存值
    local inventory = SlotTool.get_static_inventory_from_redis(gamename)

    local item = nil
    local s = inventory
    local a = 10000
    if s < -500 * a then
        item = S_list[1]
    elseif s >= -500 * a and s < -100 * a then
        item = S_list[2]
    elseif s >= -100 * a and s < -20 * a  then
        item = S_list[3]
    elseif s >= -20 * a and s < -10 * a then
        item = S_list[4]
    elseif s >= -10 * a and s < 200 * a then
        item = S_list[5]
    elseif s >= 200 * a and s < 1000 * a then
        item = S_list[6]
    elseif s >= 1000 * a and s < 5000 * a then
        item = S_list[7]
    elseif s >= 5000 * a and s < 10000 * a then
        item = S_list[8]
    elseif s >= 10000 * a then
        item = S_list[9]
    end

     --根据概率获取随机开奖类型
    local r = base.get_random(1, 1000)
    local hit = math.floor(item.hit * 1000)
    local nohit = hit + math.floor(item.nohit * 1000)
    local normal = nohit + math.floor(item.normal * 1000) 
    if r <= hit then
        return LotteryType.LOTTERY_TYPE_SYSTEM_LOSE
    elseif r <= nohit then
        return LotteryType.LOTTERY_TYPE_SYSTEM_WIN
    elseif r <= normal then
        return LotteryType.LOTTERY_TYPE_NORMAL
    end
end

--
--获取系统输赢列表
--
function SlotTool.get_system_win_lose_list(total_bet_list, total_bet, is_robot_banker)
    local sys_win_list = {}
    local sys_lose_list = {}

    if is_robot_banker then
        for id,_ in pairs(Weight_list) do
            local rtm = 0
            local bet_ids = Witem_odds_list[id] or {}
            for _,bet_id in pairs(bet_ids) do
                local odds = 2
                if odds > 0 and total_bet_list[bet_id] then
                    rtm = rtm + odds * (total_bet_list[bet_id] or 0)   --返钱数
                end
            end
            if rtm < total_bet then
                table.insert(sys_win_list,id)
            elseif rtm > total_bet then
                table.insert(sys_lose_list,id)
            end
        end
    else
        for id,_ in pairs(Weight_list) do
            local rtm = 0
            local bet_ids = Witem_odds_list[id] or {}
            for _,bet_id in pairs(bet_ids) do
                local odds = 2
                if odds > 0 and total_bet_list[bet_id] then
                    rtm = rtm + odds * (total_bet_list[bet_id] or 0)   --返钱数
                end
            end
            if rtm > total_bet then
                table.insert(sys_win_list,id)
            elseif rtm < total_bet then
                table.insert(sys_lose_list,id)
            end
        end
    end
    
    return sys_win_list,sys_lose_list
end

--
--根据手牌，获得开奖结果
--
function SlotTool.get_random_result_by_cards(total_bet_list, gamename, total_bet, lotterytype, is_robot_banker)

    --检测结果
    local function check_result(result_list, total_bet_list, total_bet, loselimit, lotterytype, is_robot_banker)
        local all_results = result_list.all_results or {}
        local ext_prize_list = result_list.ext_prize_list or {}

        --计算系统输赢
        local total_win = total_bet
        for _,id in pairs(ext_prize_list) do
            local bet_id = id
            local i = (id % 10) % 4 + 1
            local odds_item = Odds_list[id] or {}
            local cur_result = all_results[i] or {}
            local cards_type = cur_result.cards_type or 0
            local points = cur_result.points or 0
            local odds = odds_item[cards_type]
            if type(odds) == "table" then
                odds = odds[points] or odds[1] or 0
            end
            total_win = total_win - odds * (total_bet_list[bet_id] or 0)
        end

        if not is_robot_banker then
            total_win = -total_win
        end

        --判断是否超系统输上限
        local is_max_loselimit = false
        if total_win < -loselimit then
            is_max_loselimit = true
        end
        
        --不超过输上限
        if not is_max_loselimit then
            if lotterytype == LotteryType.LOTTERY_TYPE_NORMAL then
                return true
            elseif total_win >= 0 and lotterytype == LotteryType.LOTTERY_TYPE_SYSTEM_WIN then
                return true
            elseif total_win <= 0 and lotterytype == LotteryType.LOTTERY_TYPE_SYSTEM_LOSE then
                return true
            end
        end

        return false
    end

    ---------------------

    local loselimit = SlotTool.get_system_lose_limit(gamename)

    --除1234外的其他牌组合
    local all_swaps = {
        1243,1324,1342,1423,1432,2134,
        2143,2314,2341,2413,2431,3124,
        3142,3214,3241,3412,3421,4123,
        4132,4213,4231,4312,4321
    }

    local cnt = 0
    while cnt < 20 do
        local result_list = SlotTool.get_real_random_result()
        local is_ok = check_result(result_list,total_bet_list,total_bet,loselimit,lotterytype,is_robot_banker)
        if is_ok then
            return result_list
        end

        --交换牌型，再次检测
        local all_rand_cards = tabletool.deepcopy(result_list.all_cards)
        for _,val in pairs(all_swaps) do
            local k = 1000
            local cur_rand_cards = {}
            for  i = 1,4 do
                local idx = math.floor(val / k) % 10
                cur_rand_cards[i] = all_rand_cards[idx]
                k = math.floor(k / 10)
            end
            result_list = SlotTool.get_real_random_result(cur_rand_cards)
            is_ok = check_result(result_list,total_bet_list,total_bet,loselimit,lotterytype,is_robot_banker)
            if is_ok then
                return result_list
            end
        end
        cnt = cnt + 1
    end

    return false
end

--
--获得系统输钱上限
--
function SlotTool.get_system_lose_limit(gamename)
    local loselimit = 0
    local S = SlotTool.get_static_inventory_from_redis(gamename)
    if S < 0 then
        loselimit = SystemLoseLimit_Base
    else
        loselimit = math.floor(S * SystemLoseLimit_Factor) + SystemLoseLimit_Base
    end

    return loselimit
end

--
--获取系统必输随机结果
--去除超上限项
--
function SlotTool.get_mustlose_random_result(sys_lose_list, total_bet_list, gamename, total_bet, is_robot_banker)
    local loselimit = SlotTool.get_system_lose_limit(gamename)

    --去除超上限亏损
    if sys_lose_list and #sys_lose_list > 0 then
        local lose_w_list = {}
        local total_weight = 0
        for _,id in pairs(sys_lose_list) do
            local rtm = 0
            local bet_ids = Witem_odds_list[id] or {}
            for _,bet_id in pairs(bet_ids) do
                local odds = 2
                rtm = rtm + odds * (total_bet_list[bet_id] or 0)
            end
            local ksz = total_bet - rtm
            if (is_robot_banker and ksz >= -loselimit)  
            or (not is_robot_banker and ksz <= loselimit) then
                lose_w_list[id] = Weight_list[id]
                total_weight = total_weight + lose_w_list[id]
            end
        end

        if next(lose_w_list) ~= nil then
            return SlotTool.get_random_result_by_weightlist(lose_w_list,total_weight)
        else
            return SlotTool.get_min_spin_result(total_bet_list,is_robot_banker)
        end
    else
        --当前押注使系统必定赢，则选择玩家赢钱最多的结果
        return SlotTool.get_max_spin_result(total_bet_list,is_robot_banker)
    end
end

--
--获取系统必赢随机结果
--
function SlotTool.get_mustwin_random_result(sys_win_list, total_bet_list, is_robot_banker)
   if sys_win_list and #sys_win_list > 0 then
        local win_w_list = {}
        local total_weight = 0
        for _,id in pairs(sys_win_list) do
            win_w_list[id] = Weight_list[id]
            total_weight = total_weight + win_w_list[id]
        end
        
        local id = SlotTool.get_random_result_by_weightlist(win_w_list,total_weight)
        return id
    end

    --当前押注使系统必输，则选择玩家赢钱最少的结果
    return SlotTool.get_min_spin_result(total_bet_list,is_robot_banker)
end

--
--获取普通随机结果
--去掉系统超上限亏损项
--
function SlotTool.get_normal_random_result(sys_lose_list, total_bet_list, gamename, total_bet, is_robot_banker)
    local loselimit = SlotTool.get_system_lose_limit(gamename)
    local random_id_list = tabletool.deepcopy(Weight_list)

    --去除超上限亏损
    if sys_lose_list and #sys_lose_list > 0 then
        for _,id in pairs(sys_lose_list) do
            local rtm = 0
            local bet_ids = Witem_odds_list[id] or {}
            for _,bet_id in pairs(bet_ids) do
                local odds = 2
                rtm = rtm + odds * (total_bet_list[bet_id] or 0)
            end
            local ksz =  total_bet - rtm
            if is_robot_banker and ksz < -loselimit then
                random_id_list[id] = nil
            elseif not is_robot_banker and ksz > loselimit then
                random_id_list[id] = nil
            end
        end
    end

    --所有项超上限，则返回最小的
    if next(random_id_list) == nil then
        return SlotTool.get_min_spin_result(total_bet_list,is_robot_banker)
    end

    --产生随机结果
    return SlotTool.get_random_result_by_weightlist(random_id_list)
end

--
--获得纯随机结果
--
function SlotTool.get_real_random_result(all_rand_cards)
    local all_cards = {}

    if not all_rand_cards then
        -- 生成四副牌
        local initcards = {}
        sg.shuffle(initcards)
        for i = 1, 4 do
            local cur_cards = {}
            table_insert(cur_cards, table_remove(initcards, 1))
            table_insert(cur_cards, table_remove(initcards, 1))
            table_insert(cur_cards, table_remove(initcards, 1))
            sg.sort(cur_cards)
            all_cards[i] = cur_cards
        end
    else
        all_cards = all_rand_cards
    end
    
    --获得牌型
    local prize_id = 0
    local prize_list = {}
    local all_results = {}
    for i,cards in pairs(all_cards) do
       all_results[i] = {
            cards_type = sg.getcardtype(cards),
            max_card = cards[1],
            min_card = cards[3],
            points = sg.getcardvalue(cards),
        }
    end

    --判定开奖结果，第一副牌为庄家牌，其他3副为闲家
    for i = 2,4 do
        local is_win = sg.compare(all_results[1],all_results[i])
        all_results[i].is_win = is_win
        if is_win then
            table.insert(prize_list, Picture_id[i-1])
        end
    end

    --庄通杀
    if #prize_list == 0 then
        table.insert(prize_list, ESGExtraPrizeType.EXTRA_PRIZE_TYPE_KILLALL)
    end

    --返回结果
    local result_list = {
        all_cards = all_cards,
        prize_id = 0,
        ext_prize_list = prize_list,
        all_results = all_results,
    }

    return result_list
end

--
--获得可能的随机结果
--
function SlotTool.get_may_random_result(result_id)
    local all_cards = {}
    local real_all_cards = {}

    --获得获胜玩家id
    local max = 4
    local win_ids = {[1101] = 0, [1102] = 0, [1103] = 0, [1104] = 0}
    local win_list = Witem_odds_list[result_id] or {}
    for _,player_id in pairs(win_list) do
        win_ids[player_id] = max
        max = max - 1
    end
    if win_ids[1104] == 0 then
        win_ids[1104] = max
        max = max - 1
    end

    -- 生成四副牌
    local initcards = {}
    sg.shuffle(initcards)
    for i = 1, 4 do
        local cur_cards = {}
		table_insert(cur_cards, table_remove(initcards, 1))
		table_insert(cur_cards, table_remove(initcards, 1))
		table_insert(cur_cards, table_remove(initcards, 1))
		sg.sort(cur_cards)
        all_cards[i] = cur_cards
    end

    --由小到大排列牌
    table.sort(all_cards, sg.compare)
    
    --给玩家发牌
    for i = 1, 4 do
        local k = i % 4 + 1
        local id = 1100 + i
        local idx = win_ids[id]
        if idx == 0 then
            local r = base.get_random(1,max)
            real_all_cards[k] = all_cards[r]
            all_cards[r] = all_cards[max]
            all_cards[max] = real_all_cards[k]
            max = max - 1
        else
            real_all_cards[k] = all_cards[idx]
        end
    end

    --获得牌型
    local prize_id = 0
    local prize_list = {}
    local all_results = {}
    local prize_flags = {}
    for i,cards in pairs(real_all_cards) do
        all_results[i] = {
            cards_type = sg.getcardtype(cards),
            max_card = cards[1],
            min_card = cards[3],
            points = sg.getcardvalue(cards),
        }
    end

    --判定开奖结果，第一副牌为庄家牌，其他3副为闲家
    for i = 2,4 do
        local is_win = sg.compare(all_results[1],all_results[i])
        all_results[i].is_win = is_win
        if is_win then
            prize_flags[Picture_id[i-1]] = true
            table.insert(prize_list, Picture_id[i-1])
        end
    end

    --庄通杀
    if #prize_list == 0 then
        prize_flags[Picture_id[4]] = true
        table.insert(prize_list, ESGExtraPrizeType.EXTRA_PRIZE_TYPE_KILLALL)
    end

    --检测结果
    local is_ok = true
    for _,win_id in pairs(win_list) do
        if not prize_flags[win_id] then
            is_ok = false
            break
        end
    end
    
    --结果不相同，返回固定牌
    if not is_ok then
        local profile_cards = Profile_Cards_List[result_id]
        local r = base.get_random(1,#profile_cards)
        real_all_cards = tabletool.deepcopy(profile_cards[r])
        for i,cards in pairs(real_all_cards) do
            all_results[i] = SlotTool.get_cards_type(cards)
        end

        prize_list = {}
        for i = 2,4 do
            local is_win = sg.compare(all_results[1],all_results[i])
            all_results[i].is_win = is_win
            if is_win then
                table.insert(prize_list, Picture_id[i-1])
            end
        end

        --庄通杀
        if #prize_list == 0 then
            table.insert(prize_list, ESGExtraPrizeType.EXTRA_PRIZE_TYPE_KILLALL)
        end
    end

    --返回结果
    local result_list = {
        all_cards = real_all_cards,
        prize_id = 0,
        ext_prize_list = prize_list,
        all_results = all_results,
    }

    return result_list
end

--
--产生一个随机旋转结果
--
function SlotTool.gen_random_spin_result(gamename, total_bet_list, total_bet, avg_bet, is_robot_banker, is_random, banker_round)
    local result_id = nil
    local result_list = nil

    --计算系统输赢列表
    local sys_win_list,sys_lose_list = SlotTool.get_system_win_lose_list(total_bet_list,total_bet,is_robot_banker)

    --随机开奖
    if is_random then
        return SlotTool.get_real_random_result()
    else
        --获取开奖类型,分：系统必赢，系统必输，随机开奖3种
        local lottery_type = SlotTool.get_lottery_type(gamename)
        result_list = SlotTool.get_random_result_by_cards(total_bet_list,gamename,total_bet,lottery_type,is_robot_banker)
        if result_list then
            return result_list
        end

        if lottery_type == LotteryType.LOTTERY_TYPE_SYSTEM_WIN then     --系统必赢
            result_id = SlotTool.get_mustwin_random_result(sys_win_list,total_bet_list,is_robot_banker)
        elseif lottery_type == LotteryType.LOTTERY_TYPE_SYSTEM_LOSE then  --系统必输
            result_id = SlotTool.get_mustlose_random_result(sys_lose_list,total_bet_list,gamename,total_bet,is_robot_banker)
        else    --随机开奖
            result_id = SlotTool.get_normal_random_result(sys_lose_list,total_bet_list,gamename,total_bet,is_robot_banker)
        end
    end

    --根据开牌结果获取随机牌
    local result_list = SlotTool.get_may_random_result(result_id)

    return result_list
end

--
--获取最小奖励
--
function SlotTool.get_min_spin_result(total_bet_list, is_robot_banker)
    local min_id = 0
    local min_rtm = 100000000000
    if not is_robot_banker then
        min_rtm = 0
    end
    
    --循环每一种情况,求返钱数最小的
    for id,_ in pairs(Weight_list) do
        local rtm = 0
        local bet_ids = Witem_odds_list[id] or {}
        for _,bet_id in pairs(bet_ids) do
            local odds = 2
            if odds > 0 and total_bet_list[bet_id] then
                rtm = rtm + odds * (total_bet_list[bet_id] or 0)   --返钱数
            end
        end
        if is_robot_banker then
            if min_rtm > rtm then
                min_rtm = rtm
                min_id = id
            end
        else
            if min_rtm < rtm then
                min_rtm = rtm
                min_id = id
            end
        end
        
    end
    
    return min_id
end

--
--获取最大奖励
--
function SlotTool.get_max_spin_result(total_bet_list, is_robot_banker)
    local max_id = 0
    local max_rtm = 0
    if not is_robot_banker then
        max_rtm = 100000000000
    end
    
    --循环每一种情况,求返钱数最大的
    for id,_ in pairs(Weight_list) do
        local rtm = 0
        local bet_ids = Witem_odds_list[id] or {}
        for _,bet_id in pairs(bet_ids) do
            local odds = 2
            if odds > 0 and total_bet_list[bet_id] then
                rtm = rtm + odds * (total_bet_list[bet_id] or 0)   --返钱数
            end
        end
        if is_robot_banker then
            if max_rtm < rtm then
                max_rtm = rtm
                max_id = id
            end
        else
            if max_rtm > rtm then
                max_rtm = rtm
                max_id = id
            end
        end
    end
    
    return max_id
end

--
--保存奖励记录
--
function SlotTool.save_prize_records(gamename, opencard_result)
    local records = {}
    local key = (gamename or "") .. "_prize_records"
    local result,data = redisdao.query_data(".gmmqredis", "get", key)
    if result and data then
        records = json.decode(data) or {}
    end
    
    for _,prize_id in pairs(opencard_result) do
        local id = tostring(prize_id)
        records[id] = (tonumber(records[id]) or 0) + 1
    end
    records.total_count = (tonumber(records.total_count) or 0) + 1
    redisdao.query_data(".gmmqredis", "set", key, json.encode(records))

    ---------------------------------

    --辅助函数1
    local function helper_func(val)
        local cnt = 0
        local tmp = val
        while val >= 10 do
            val = math.floor(val / 10)
            cnt = cnt + 1
        end
        if math.floor(10^cnt) == tmp then
            return true
        end
        return false
    end

    --辅助函数2
    local function func(records)
        local porp_list = {}
        local subprop_list = {}
        local cnt = records.total_count or 0
        if cnt == 0 then
            return
        end

        for k,v in pairs(records) do
            if type(v) ~= "table" and k ~= "total_count" then
                v = tonumber(v) or 0
                porp_list[k] = string.format("%g%%",math.floor(v / cnt * 10000) / 100)
            elseif k == "total_count" then
                porp_list[k] = v
            else
                subprop_list[k] = func(v)
            end
        end
        return porp_list,subprop_list
    end

    --开奖次数每增加10倍，统计一次概率
    if helper_func(records.total_count) then
        local porp_list,subprop_list = func(records)
        filelog.sys_obj("prop_list", records.total_count, porp_list, "=================\n", subprop_list)
    end
    ---------------

    return true
end

--
--创建格式化赔率列表
--
function SlotTool.create_format_odds_list()
    local fmt_odds_list = {}

    for id,odds_list in pairs(Odds_list) do
        for cardstype,odds in pairs(odds_list) do
            if type(odds) == "table" then
                for i = 0,9 do
                    local real_odds = odds[i] or odds[1] or 2
                    local val = tonumber(id) * 10000 + cardstype * 1000 + i * 100 + tonumber(real_odds)
                    table.insert(fmt_odds_list,val)
                end
            else
                for i = 0,9 do
                    local val = tonumber(id) * 10000 + cardstype * 1000 + i * 100 + tonumber(odds)
                    table.insert(fmt_odds_list,val)
                end
            end
        end
    end
    
    return fmt_odds_list
end

--
--保存赔率列表到文件
--
function SlotTool.save_odds_list_to_file()
    local f = io.open("odds_list.lua","w+")
    if not f then
        filelog.print("open file error!")
        return
    end

    f:write("return { \n")
    for id,odds_list in pairs(Odds_list) do
        for cardstype,odds in pairs(odds_list) do
            if type(odds) == "table" then
                for i = 0,9 do
                    local real_odds = odds[i] or odds[1] or 2
                    local val = tonumber(id) * 10000 + cardstype * 1000 + i * 100 + tonumber(real_odds)
                     f:write(tostring(val) .. ",")
                end
            else
                for i = 0,9 do
                    local val = tonumber(id) * 10000 + cardstype * 1000 + i * 100 + tonumber(odds)
                     f:write(tostring(val) .. ",")
                end
            end
        end
    end
    f:write("}")
    f:close()
end


-------------------------------
----------
--外部接口

local CMD = {}

local cs = queue()

-----------------
----

--
--获取随机旋转结果
--total_bet_list 总下注列表
--total_bet 总下注值
--avg_bet 平均下注值
--is_robot_banker 是否机器人做庄
--is_random 是否随机开奖
--banker_round 已做庄局数
--
function CMD.get_random_spin_result(gamename, total_bet_list, total_bet, avg_bet, is_robot_banker, is_random, banker_round)
    local cnt = 0
    while cnt < 100 do
        local isok,spin_result = skynet.pcall(SlotTool.gen_random_spin_result,gamename,total_bet_list,total_bet,avg_bet,is_robot_banker, is_random, banker_round)
        if isok then
            return spin_result
        end
        cnt = cnt + 1
    end

    return false
end

--
--处理开奖
--
function CMD.handle_open_jackpot(gamename, jackpot_id, player_list)
    local award_list = {}

    --获取奖池金额
    local jackpot_money = SlotTool.get_jackpot_from_redis(gamename,jackpot_id)

    --随机开奖
    local total_coin = 0
    local max_level = 6
    for _,item in pairs(player_list) do
        local level = math.floor(item.game_num / 5)
        if level > max_level then
            level = max_level
        end
        local w_list = Jackpot_weight_list[level]
        local award_id = SlotTool.get_random_result_by_weightlist(w_list)
        local award_coin = math.floor(Jackpot_award_list[award_id] * jackpot_money * 0.01)
        local award_item = {
            rid = item.rid,
            logo = item.logo,
            rolename = item.rolename,
            award_money_num = award_coin,
        }
        table.insert(award_list, award_item)
        total_coin = total_coin + award_coin
    end
    
    return award_list,total_coin
end

--
--保存奖励记录
--
function CMD.save_prize_records(gamename, opencard_result)
  return cs(SlotTool.save_prize_records,gamename,opencard_result)
end

--
--处理庄家收益
--
function CMD.handle_banker_profit(ganmename, total_bet_num, player_win_num)
    local banker_win = total_bet_num - player_win_num
    return banker_win
end

--
--更新图标权值表
--
function CMD.update_picture_weight_list(new_w_list)
    if type(new_w_list) ~= "table" then
        return false
    end

    for k,v in pairs(new_w_list) do
        if Weight_list[k] and v >= 0 then
           Weight_list[k] = v
        end
    end
    
    --更新化概率表,总权值
    if next(new_w_list) ~= nil then
        Prob_list,Total_weight_list = SlotTool.update_picture_prob_list()
    end

    return true
end

--
--设置系统输钱上限
--
function CMD.set_system_lose_limit(loselimit_base, loselimit_factor)
    loselimit_base = loselimit_base or 0
    loselimit_factor = loselimit_factor or 0.02
    if loselimit_base < 0 or loselimit_factor < 0 then
        return false
    end
    SystemLoseLimit_Base = loselimit_base
    SystemLoseLimit_Factor = loselimit_factor

    return true
end

--
--获取格式化赔率列表
--
function CMD.get_format_odds_list()
    return SlotTool.create_format_odds_list()
end

--
--处理结算结果
--@total_change 总变化值
--@total_water  总流水值
--@total_bet    玩家总下注
--
function CMD.handle_balance_result(gamename, total_change, total_water, total_bet, comment)
    local tax_rate = SlotTool.get_tax_rate_from_redis(gamename)
    local profit_val = math.floor(total_bet * tax_rate * 100) / 100
    local inventory_val = -total_change

    --更新收益值
    local result1 = SlotTool.update_system_profit(gamename, profit_val, comment)
    if result1 then
        inventory_val = inventory_val - profit_val
    end

    --更新库存值，扣除抽水
    local service_fee_rate = SlotTool.get_service_fee_rate_from_redis(gamename)
    local service_fee_val = math.floor(total_water * service_fee_rate * 100) / 100
    inventory_val = inventory_val - service_fee_val

    return SlotTool.update_system_inventory(gamename, inventory_val, comment)
end

--
--获取抽水率
--
function CMD.get_service_fee_rate(gamename)
    return SlotTool.get_service_fee_rate_from_redis(gamename)
end

-----------
--
--初始化
--
local function init()

    --初始化概率表,总权值
    Prob_list,Total_weight = SlotTool.update_picture_prob_list()
end

-------------------
--
skynet.start(function()
    init()
    skynet.dispatch("lua",function(session, source, cmd, ...)
        local f = CMD[cmd]
        if f then
            skynet.retpack(f(...))
        else
            skynet.retpack()
        end
    end)
end)